- 本文介绍
react-native-camera
扫码的应用- 更多详情文档 https://github.com/react-native-community/react-native-camera
效果展示
配置
react-native link react-native-camera
@CodePoet: 代码已经复制到剪贴板
// 配置andriod/app/src/build.gradle defaultConfig { applicationId "com.jtyapps" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion versionCode 1 versionName "1.0" ndk { abiFilters "armeabi-v7a", "x86" } // 添加这里 missingDimensionStrategy 'react-native-camera', 'general' } // 配置andriod/gradle/wrapper/gradle-wrapper.properties // 本教程使用的是这个版本 distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip
@CodePoet: 代码已经复制到剪贴板
添加相机权限
andriod/app/src/main/AndroidManifest.xml
<uses-permission android:name="android.permission.BLUETOOTH"/> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/> <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGEE"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/> <uses-permission android:name="android.permission.CAMERA"/> <uses-permission android:name="android.permission.VIBRATE"/> <uses-feature android:name="android.hardware.camera" android:required="false"/> <uses-feature android:name="android.hardware.camera.front" android:required="false"/>
@CodePoet: 代码已经复制到剪贴板
处理逻辑
// scan.js import React, { Component } from 'react'; import { View, Text, StyleSheet, Image, Platform, Vibration, TouchableOpacity, Animated, Easing, Dimensions, StatusBar } from 'react-native'; import Icon from 'react-native-vector-icons/Feather'; const { width, height } = Dimensions.get('window'); import { Modal } from '@ant-design/react-native'; import {RNCamera} from 'react-native-camera'; import ViewFinder from './ViewFinder'; import scanLine from '../../assets/images/scan_line.png'; import { scaleSizeW, scaleSizeH, setSpText } from '../../utils/scale'; export default class MyScan extends Component { static navigationOptions = ({ navigation, navigationOptions }) => { return({ header: null }) }; constructor(props) { super(props); this.camera = null; this.state = { openFlash: false, active: true, flag: true, fadeInOpacity: new Animated.Value(0), // 初始值 isEndAnimation: false,//结束动画标记 key: '' } } componentDidMount() { const {navigation} = this.props; this._startAnimation(false); } componentWillReceiveProps(nProps) { const {key } = this.state; const {navigation} = nProps; let newKey = navigation.getParam('key'); if(key!=newKey){ this.transCode = ''; this.setState({ key: newKey }) } console.log('scan进来了') } //开始动画,循环播放 _startAnimation = (isEnd) => { Animated.timing(this.state.fadeInOpacity, { toValue: 1, duration: 3000, easing: Easing.linear }).start( () => { if (isEnd) { this.setState({ isEndAnimation: true }) return; } if (!this.state.isEndAnimation) { this.state.fadeInOpacity.setValue(0); this._startAnimation(false) } } ); // console.log("开始动画"); } barcodeReceived (e){ const {navigation:{navigate}} = this.props; if (e.data !== this.transCode) { Vibration.vibrate([0, 500, 200, 500]); this.transCode = e.data; // 放在this上,防止触发多次,setstate有延时 if (this.state.flag) { this.changeState(false); } console.log("transCode=" + this.transCode); Modal.alert('温馨提示', `确定绑定: ${this.transCode} 吗?`, [ { text: '取消', onPress: () => console.log('cancel'), style: 'cancel', }, { text: '确定', onPress: () => { navigate('Record') // dispatch({ // type: 'user/bindHeartDev', // callback: res=>{ // if(res.code == 200) { // navigate('Record') // } // } // }) } }, ]); } } //返回按钮点击事件 _goBack = () => { this.setState({ isEndAnimation: true, }); this.props.navigation.goBack(); } //开灯关灯 _changeFlash = () => { this.setState({ openFlash: !this.state.openFlash, }); } //改变请求状态 changeState = (status) => { this.setState({ flag: status }); console.log('status=' + status); } render() { const { openFlash, active, } = this.state; return ( <View style={styles.allContainer}> <RNCamera barCodeTypes={[RNCamera.Constants.BarCodeType.qr]} onBarCodeRead={this.barcodeReceived.bind(this)} onCameraReady={() => { console.log('ready') }} permissionDialogTitle={'Permission to use camera'} permissionDialogMessage={'We need your permission to use your camera phone'} // defaultVideoQuality={RNCamera.Constants.VideoQuality["480p"]} style={styles.cameraStyle} > <View style={styles.container}> <View style={styles.titleContainer}> <View style={styles.leftContainer}> <TouchableOpacity activeOpacity={1} onPress={this._goBack}> <View> {/* <Image style={styles.backImg} source={backIcon} /> */} <Icon name="chevron-left" style={styles.backIcon}/> </View> </TouchableOpacity> </View> </View> </View> <View style={styles.centerContainer} /> <View style={{ flexDirection: 'row' }}> <View style={styles.fillView} /> <View style={styles.scan}> <ViewFinder /> <Animated.View style={[styles.scanLine, { opacity: 1, transform: [{ translateY: this.state.fadeInOpacity.interpolate({ inputRange: [0, 1], outputRange: [0, scaleSizeH(300)] }) }] }]}> <Image source={scanLine} /> </Animated.View> </View> <View style={styles.fillView} /> </View> <View style={styles.bottomContainer}> <Text style={[ styles.text, { textAlign: 'center', width: 220, marginTop: active ? 25 : 245, }, ]} numberOfLines={2} > 请将手表二维码放入扫码框内 </Text> {/** <TouchableOpacity onPress={this._changeFlash}> <View style={styles.flash}> <Text style={styles.icon}>(・◇・)</Text> <Text style={styles.text}> 开灯/关灯 </Text> </View> </TouchableOpacity> */} </View> </RNCamera> </View> ) } } const styles = StyleSheet.create({ allContainer: { flex: 1, }, container: { ...Platform.select({ ios: { height: 64 + StatusBar.currentHeight, }, android: { height: 50 + StatusBar.currentHeight, } }), backgroundColor: '#000', opacity: 0.5 }, titleContainer: { flex: 1, ...Platform.select({ ios: { paddingTop: 15, }, android: { paddingTop: 0, } }), flexDirection: 'row', }, leftContainer: { flex: 0, justifyContent: 'center', }, backIcon: { marginLeft: 10, fontSize: 30, color: '#fff' }, cameraStyle: { alignSelf: 'center', width: width, height: height, }, flash: { flexDirection: 'column', alignItems: 'center', justifyContent: 'flex-start', marginTop: 60, }, flashIcon: { fontSize: 1, color: '#fff', }, text: { fontSize: 14, color: '#fff', marginTop: 5 }, icon: { color: '#fff', fontSize: 20, fontFamily: 'iconfont' }, scanLine: { alignSelf: 'center', }, centerContainer: { ...Platform.select({ ios: { height: 80, }, android: { height: 60, } }), width: width, backgroundColor: '#000', opacity: 0.5 }, bottomContainer: { alignItems: 'center', backgroundColor: '#000', alignSelf: 'center', opacity: 0.5, flex: 1, width: width }, fillView: { width: (width - scaleSizeH(300)) / 2, height: scaleSizeH(300), backgroundColor: '#000', opacity: 0.5 }, scan: { width: scaleSizeH(300), height: scaleSizeH(300), alignSelf: 'center' }, info:{ color: '#fff' } })
@CodePoet: 代码已经复制到剪贴板
// ViewFinder.js import React, { Component, } from 'react'; import { ActivityIndicator, StyleSheet, View, } from 'react-native'; import { scaleSizeW, scaleSizeH, setSpText } from '../../utils/scale'; export default class Viewfinder extends Component { constructor(props) { super(props); } getBackgroundColor = () => { return ({ backgroundColor: this.props.backgroundColor, }); } getEdgeColor = () => { return ({ borderColor: this.props.color, }); } getSizeStyles = () => { return ({ height: this.props.height, width: this.props.width, }); } getEdgeSizeStyles = () => { return ({ height: this.props.borderLength, width: this.props.borderLength, }); } renderLoadingIndicator = () => { if (!this.props.isLoading) { return null; } return ( <ActivityIndicator animating={this.props.isLoading} color={this.props.color} size="large" /> ); } render() { return ( <View style={[styles.container, this.getBackgroundColor()]}> <View style={[styles.viewfinder, this.getSizeStyles()]}> <View style={[ this.getEdgeColor(), this.getEdgeSizeStyles(), styles.topLeftEdge, { borderLeftWidth: this.props.borderWidth, borderTopWidth: this.props.borderWidth, }, ]} /> <View style={[ this.getEdgeColor(), this.getEdgeSizeStyles(), styles.topRightEdge, { borderRightWidth: this.props.borderWidth, borderTopWidth: this.props.borderWidth, }, ]} /> {this.renderLoadingIndicator()} <View style={[ this.getEdgeColor(), this.getEdgeSizeStyles(), styles.bottomLeftEdge, { borderLeftWidth: this.props.borderWidth, borderBottomWidth: this.props.borderWidth, }, ]} /> <View style={[ this.getEdgeColor(), this.getEdgeSizeStyles(), styles.bottomRightEdge, { borderRightWidth: this.props.borderWidth, borderBottomWidth: this.props.borderWidth, }, ]} /> </View> </View> ); } } const styles = StyleSheet.create({ container: { alignItems: 'center', justifyContent: 'center', position: 'absolute', top: 0, right: 0, bottom: 0, left: 0, }, viewfinder: { alignItems: 'center', justifyContent: 'center', backgroundColor: 'transparent', }, topLeftEdge: { position: 'absolute', top: 0, left: 0, }, topRightEdge: { position: 'absolute', top: 0, right: 0, }, bottomLeftEdge: { position: 'absolute', bottom: 0, left: 0, }, bottomRightEdge: { position: 'absolute', bottom: 0, right: 0, }, }); Viewfinder.defaultProps = { backgroundColor: 'transparent', borderWidth: 3, borderLength: 20, color: '#1DBAF1', height: scaleSizeH(300), isLoading: false, width: scaleSizeH(300), };
@CodePoet: 代码已经复制到剪贴板
// 使用方式 onScan = ()=>{ const { navigate } = this.props.navigation; // 跳转到扫码页面即可打开 navigate('Scan') }
@CodePoet: 代码已经复制到剪贴板
阅读全文